#if defined(_MSC_VER)
#pragma once
#endif

#ifndef LM_CORE_CAMERA_H
#define LM_CORE_CAMERA_H

#include "lm.h"
#include "sampler.h"
#include "transform.h"
#include "memory.h"

class Camera
{
public:
	Camera(const Transform &w2c, float cNear, float cFar, float sOpen, float sClose, Reference<Film> fm);
	virtual ~Camera();
	void SetFilm(Reference<Film> fm);
	inline Point3 GetPosition() const {
		return position;
	}
	inline void SetTransform(const Transform &v) {
		worldToCamera = v;
		cameraToWorld = v.Inverse();
		position = cameraToWorld(Point3(0,0,0));
	}
	inline Transform GetTransform() const {
		return worldToCamera;
	}
	// we can use this information to initialize sampler
	inline bool NeedTimeSample() const {
		return (shutterClose - shutterOpen) > 0.0;
	}
	inline float getClipNear() const {
		return clipNear;
	}
	inline float getClipFar() const {
		return clipFar;
	}
	float GenerateDifferentialRay(const Point2 &imageSample, const Point2 &lensSample, float timeSample, RayDifferential *ray) const;

	// we can use this information to initialize sampler
	virtual bool NeedLensSample() const = 0;
	virtual float GenerateRay(const Point2 &imageSample, const Point2 &lensSample, float timeSample, Ray *ray) const = 0;
	std::string ToString() const {
		std::ostringstream oss;
		oss << "Unimplemented ToString functions in class Camera";
		Warning("%s\n",oss.str().c_str());
		return oss.str();
	}

public:
	//Film *film;
	Reference<Film> film;
	Transform worldToCamera, cameraToWorld;
	float clipNear, clipFar;
	float shutterOpen, shutterClose;
	Point3 position;
};

class ProjectiveCamera : public Camera
{
public:
	ProjectiveCamera(const Transform &w2c, const Transform &c2s, 
		float cNear, float cFar, float sOpen, float sClose, float lensr, float focald, Film *fm);
	Transform cameraToScreen, rasterToCamera;
	Transform screenToRaster, rasterToScreen;
	Transform worldToRaster, rasterToWorld;
	Transform worldToScreen, screenToWorld;
	/*
	 *	Note that : Transforms follow as sequence below:
	 *	
	 *	Transform			worldToCamera				cameraToScreen					screenToRaster
	 *	Space		world ------------------> camera --------------------> screen ---------> NDC ----------> raster
	 *	From				look at						perspective/eta					aspect
	 *	
	 *	We have worldToCamera, cameraToWorld, cameraToScreen, rasterToCamera, screenToRaster, rasterToScreen
	 *	other transforms can be generated by using:
	 *		screenToCamera = cameraToScreen.Inverse();
	 *		worldToScreen = cameraToScreen * worldToCamera;
	 *	so on and so fourth.
	 */
	float lensRadius, focalDistance;
};

#endif	// LM_CORE_CAMERA_H